namespace ScrumSprintMonitor.Samples.Speech
{
	#region #using Directives

	using System;
	using System.Diagnostics;
	using System.Globalization;
	using System.Speech.Synthesis;

	using ContinuousLinq.Reactive;

	using Infrastructure;

	#endregion

	internal class SpeechService : ReactiveObject,
	                               IDisposable
	{
		private readonly SpeechSynthesizer synthesizer;
		private BuildStatus lastBuildStatus = BuildStatus.NotSetUp;
		private IReportableData reportableData;

		static SpeechService()
		{
			var dependsOn = Register<SpeechService>();

			dependsOn.Call(me => me.OnLastBuildStatusChanged()).OnChanged(me => me.ReportableData.LastBuild.Status);
		}

		public SpeechService(IReportableData reportableData)
		{
			if (reportableData == null)
			{
				throw new ArgumentNullException("reportableData");
			}

			this.synthesizer = new SpeechSynthesizer {
			                                         	Volume = 100
			                                         };
			this.synthesizer.SetOutputToDefaultAudioDevice();
			this.synthesizer.SelectVoiceByHints(VoiceGender.Female, VoiceAge.Adult);

			ReportableData = reportableData;
		}

		private IReportableData ReportableData
		{
			[DebuggerStepThrough]
			get { return this.reportableData; }
			set
			{
				if (value == ReportableData)
				{
					return;
				}

				this.reportableData = value;

				OnPropertyChanged("ReportableData");
			}
		}

		~SpeechService()
		{
			Dispose(false);
		}

		private void OnLastBuildStatusChanged()
		{
			var status = BuildStatus.NotSetUp;
			string requestedFor = string.Empty;
			string buildNumber = string.Empty;
			if (ReportableData != null && ReportableData.LastBuild != null)
			{
				status = ReportableData.LastBuild.Status;
				requestedFor = ReportableData.LastBuild.RequestedFor;
				buildNumber = ReportableData.LastBuild.Number;
			}

			if (status == BuildStatus.NotSetUp)
			{
				return;
			}

			var promptBuilder = new PromptBuilder(CultureInfo.GetCultureInfo("en-us"));
			switch (status)
			{
				case BuildStatus.NotSetUp:
					break;
				case BuildStatus.Failed:
					promptBuilder.AppendText(string.Format("{0} broke the build {1}!", requestedFor, buildNumber), PromptEmphasis.Strong);
					break;
				case BuildStatus.NotStarted:
					break;
				case BuildStatus.InProgress:
					break;
				case BuildStatus.PartiallySucceeded:
					promptBuilder.AppendText(string.Format("{0} has broken the tests on build {1}!", requestedFor, buildNumber), PromptEmphasis.Strong);
					break;
				case BuildStatus.Stopped:
					break;
				case BuildStatus.Succeeded:
					if (this.lastBuildStatus == BuildStatus.Failed || this.lastBuildStatus == BuildStatus.PartiallySucceeded)
					{
						promptBuilder.AppendText(string.Format("{0} has fixed the build {1}.", requestedFor, buildNumber), PromptEmphasis.Reduced);
					}
					break;
			}

			this.lastBuildStatus = status;

			if (!promptBuilder.IsEmpty)
			{
				this.synthesizer.SpeakAsync(promptBuilder);
			}
		}

		#region Implementation of IDisposable

		/// <summary>
		/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
		/// </summary>
		/// <filterpriority>2</filterpriority>
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		private void Dispose(bool disposing)
		{
			if (disposing)
			{
				this.synthesizer.Dispose();
			}
		}

		#endregion
	}
}